/*
 * Builder.h
 *
 *  Created on: 2015年6月11日
 *      Author: chuanjiang.zh@qq.com
 */

#ifndef Builder_H_
#define Builder_H_

#include <string>
#include <sstream>
#include <map>

namespace xml
{

typedef std::map< std::string, std::string>  StringMap;


class Attributes : public StringMap
{
public:
    Attributes(const char* key, const std::string& value)
    {
        set(key, value);
    }

    Attributes(const std::string& key, const std::string& value)
    {
        set(key, value);
    }

    template < class T >
    Attributes(const std::string& key, const T& value)
    {
        set(key, value);
    }

    void set(const char* key, const std::string& value)
    {
        (*this)[key] = value;
    }

    void set(const std::string& key, const std::string& value)
    {
        (*this)[key] = value;
    }

    template < class T >
    void set(const std::string& key, const T& value)
    {
        std::ostringstream  ostream;
        ostream << value;
        set(key, ostream.str());
    }
};

std::ostream& operator << (std::ostream& os, const Attributes& attr);


class Document
{
public:
    enum Option
    {
        NONE = 0,
        VERSION,
        DECLARATION
    };

public:
    explicit Document(Option opt = NONE);
    explicit Document(const char* declaration);
    explicit Document(const std::string& declaration);
    virtual ~Document();

    std::string str();


    template < class T >
    Document& write(const char* tag, const T& t)
    {
        m_ostream << "<" << tag << ">";
        m_ostream << t;
        m_ostream << "</" << tag << ">\n";
        return *this;
    }


    template < class T >
    Document& write(const std::string& tag, const T& t)
    {
        m_ostream << "<" << tag << ">";
        m_ostream << t;
        m_ostream << "</" << tag << ">\n";
        return *this;
    }

protected:
    friend class Node;

    std::ostream& getStream();

    std::ostringstream  m_ostream;


};


class Node
{
public:
    Node(Document& doc, const std::string& tag);
    Node(Document& doc, const std::string& tag, const Attributes& attr);
    Node(Document& doc, const std::string& tag, const Attributes& attr, const std::string& text);
    Node(Document& doc, const std::string& tag, const std::string& text);
    ~Node();

    template < class T >
    Node& text(const T& t)
    {
        if (isOpen())
        {
            m_ostream << t;

            close();
        }
        return *this;
    }
 
    template < class T >
    Node& write(const char* tag, const T& t)
    {
        if (isOpen())
        {
            if (m_firstChild)
            {
                m_ostream << "\n";
                m_firstChild = false;
            }

            m_ostream << "<" << tag << ">";
            m_ostream << t;
            m_ostream << "</" << tag << ">";
            m_ostream << "\n";
        }
        return *this;
    }

    template < class T >
    Node& write(const std::string& tag, const T& t)
    {
        if (isOpen())
        {
            if (m_firstChild)
            {
                m_ostream << "\n";
                m_firstChild = false;
            }

            m_ostream << "<" << tag << ">";
            m_ostream << t;
            m_ostream << "</" << tag << ">";
            m_ostream << "\n";
        }
        return *this;
    }

    void close();

    bool isOpen() const;

protected:
    std::ostream&   m_ostream;
    std::string m_tag;
    bool m_closed;
    bool m_isText;
    bool m_firstChild;

};





}

#endif /* Builder_H_ */
